package com.example.iceman.stepcounterapp;

/**
 * Created by iceman on 16/2/17.
 * 重力感应数据计算工具类
 */
public class StepCalcu {
    private NewStepCounter stepCounter;
    /**
     * 默认的步伐间隔,最短300毫秒一步,当走路幅度较小时,这个值还会增加
     */
    private int defaultStepInterval = 300;

    final int valueNum = 4;
    //是否上升的标志位
    boolean isDirectionUp = false;
    //持续上升次数
    int continueUpCount = 0;

    int coutinueDownCount = 0;
    //上一点的持续上升的次数，为了记录波峰的上升次数
    int continueUpFormerCount = 0;
    //上一点的持续下降的次数，为了记录波峰的下降次数
    int continueDownFormerCount = 0;
    //上一点的状态，上升还是下降
    boolean lastStatus = false;
    //波峰值
    float peakOfWave = 0;
    //波谷值
    float valleyOfWave = 0;
    //此次波峰的时间
    long timeOfThisPeak = 0;
    //上次波峰的时间
    long timeOfLastPeak = 0;
    //当前的时间
    long timeOfNow = 0;
    //上次传感器的值
    float gravityOld = 0;


    boolean isPeak = false;
    private int tempCount;
    private float[] tempValue = new float[valueNum];

    public StepCalcu(NewStepCounter stepCounter, int stepInterval) {
        this.stepCounter = stepCounter;
        this.defaultStepInterval = stepInterval;
    }


    /**
     * 检测到一个波峰以后，判断其是否是一个有效的步子
     * 判断逻辑1:波峰和波谷的差值是否在范围内
     * 判断逻辑2:距离上次波峰的时间是否达到要求，排除快速抖动
     *
     * @param values
     */
    public void DetectorNewStep(float values) {
        if (gravityOld == 0) {
            gravityOld = values;
        }
        if (DetectorPeak(values, gravityOld)) {
            Log("检测到波峰:" + peakOfWave + ",波谷:" + valleyOfWave);
            float ratio = peakOfWave / valleyOfWave;
            if (ratio < 1.1f) {
                Log("波峰波谷比率不符合要求,pass");
                return;
            }
            float currentAverageRatio = peakValleyRatio(ratio);
            float peakrange = valleyOfWave * currentAverageRatio;
            if (!isPeak && peakOfWave >= peakrange * 0.8 && peakOfWave <= peakrange * 1.2) {
                if (timeOfLastPeak == 0) {
                    timeOfLastPeak = System.currentTimeMillis();
                } else {
                    timeOfLastPeak = timeOfThisPeak;
                }
                timeOfNow = System.currentTimeMillis();
                Log("波峰满足要求,当前时间:" + timeOfNow + ";上次波峰时间:" + timeOfLastPeak);
                if (timeOfNow - timeOfLastPeak >= getFilterTimeByPeakRatio(currentAverageRatio)) {
                    step();
                    Log("步数增加!!!!!!!!!!!!!!!!!!!!!!");
                    isPeak = true;
                }
                timeOfThisPeak = System.currentTimeMillis();
            }
        }
        gravityOld = values;
    }

    private void Log(String s) {
        stepCounter.onLog(s);
    }


    private void step() {
        stepCounter.onStep();
    }

    /**
     * 根据当前使用的波峰波谷比率,来设定步伐检测间隔
     * 按常理,走路的幅度和速度是成正比的,此举也是为了防止拿在手中时灵敏度过高
     *
     * @param ratio
     * @return 波峰之间的时间间隔要求, 单位为毫秒
     */
    public int getFilterTimeByPeakRatio(float ratio) {
        if (ratio < 1.3) {
            return (int) (defaultStepInterval * 1.6);
        } else if (ratio < 1.5) {
            return (int) (defaultStepInterval * 1.3);
        } else {
            return defaultStepInterval;
        }
    }


    /**
     * 对波峰波谷的比率进行动态调整,因为慢步走和快步走的差距太大了...
     * 取最近四次波峰波谷比率进行平均值计算
     *
     * @param value
     * @return
     */
    public float peakValleyRatio(float value) {
        if (tempCount < valueNum) {
            tempValue[tempCount] = value;
            tempCount++;
        } else {
            for (int i = 1; i < valueNum; i++) {
                tempValue[i - 1] = tempValue[i];
            }
            tempValue[valueNum - 1] = value;
        }
        float averageRatio = averageValue(tempValue, tempCount);
        Log("当前适用比率:" + averageRatio + ",当前过滤时间:" + getFilterTimeByPeakRatio(averageRatio));
        return averageRatio;

    }

    public float averageValue(float value[], int n) {
        if (n == 0) {
            return 0;
        }
        float ave = 0;
        for (int i = 0; i < n; i++) {
            ave += value[i];
        }
        ave = ave / n;
        return ave;
    }

    /**
     * 检测波峰
     * 以下三个条件判断为波峰：
     * 1.目前点为下降的趋势：isDirectionUp为false
     * 2.之前的点为上升的趋势：lastStatus为true
     * 3.到波峰为止，持续上升大于等于2次，排除抖动
     * 记录波谷值
     * 1.观察波形图，可以发现在出现步子的地方，波谷的下一个就是波峰，有比较明显的特征以及差值
     * 2.所以要记录每次的波谷值，为了和下次的波峰做对比
     */

    public boolean DetectorPeak(float newValue, float oldValue) {
        lastStatus = isDirectionUp;
        if (newValue >= oldValue) {
            continueUpCount++;
            continueDownFormerCount = coutinueDownCount;
            isDirectionUp = true;
            Log("up:" + oldValue + "->" + newValue + ":" + continueUpCount);
        } else {
            coutinueDownCount++;
            continueUpFormerCount = continueUpCount;
            continueUpCount = 0;
            isDirectionUp = false;
            Log("down:" + oldValue + "->" + newValue + ":" + continueUpCount);
        }
        if (!isDirectionUp && lastStatus
                && (continueUpFormerCount >= 2) && valleyOfWave != 0) {
            peakOfWave = oldValue;
            return true;
        } else if (!lastStatus && isDirectionUp && continueDownFormerCount >= 2) {
            valleyOfWave = oldValue;
            isPeak = false;
            return false;
        } else {
            return false;
        }
    }
}
